#include "InputSystem.h"

extern HWND mainHwnd;

/// <Summary> The instance of this class </Summary>
InputSystem* InputSystem::instance = NULL;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Gets the instance of this class </Summary>
///
/// <Return>	The instance of this class </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
InputSystem& InputSystem::GetInstance()
{
	if(NULL == instance)
	{
		instance = new InputSystem;
	}
	return *instance;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Deletes the instance of this class </Summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::DeleteInstance()
{
	SAFE_DELETE(instance);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Initializes the input manager to accept input events </Summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::Initialize()
{
	RAWINPUTDEVICE Rid[2];

	// Keyboard
	Rid[0].usUsagePage = 1;
	Rid[0].usUsage = 6;
	Rid[0].dwFlags = 0;
	Rid[0].hwndTarget = mainHwnd;

	// Mouse
	Rid[1].usUsagePage = 1;
	Rid[1].usUsage = 2;
	Rid[1].dwFlags = 0;
	Rid[1].hwndTarget = mainHwnd;

	BOOL result = RegisterRawInputDevices(Rid,2,sizeof(RAWINPUTDEVICE));

	for(unsigned short i = 0; i < 256; ++i)
	{
		keyMap[i] = KeyboardData(i, WM_KEYUP);
	}

	for(unsigned short i = 0; i < MOUSE_MAX; ++i)
	{
		mouseMap[i] = MouseData((MouseButton)i);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Shuts down the input manager </Summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::Shutdown()
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Updates the input manager with a key define by the windows LPARAM </Summary>
///
/// <Param name="lParam">	The lParam field of the message </Param>
/// 
/// <Remarks> 
///		This function should be called inside the windows message loop 
///		whenever a WM_INPUT message is recieved
///	</Remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::Update(LPARAM lParam)
{
	UINT bufferSize = 40;
	BYTE g_RawInputBuffer[40];
	GetRawInputData((HRAWINPUT)lParam, RID_INPUT, (LPVOID)g_RawInputBuffer,
		&bufferSize, sizeof(RAWINPUTHEADER));

	RAWINPUT *raw = (RAWINPUT*) g_RawInputBuffer;

	if( raw->header.dwType == (raw->header.dwType & RIM_TYPEKEYBOARD))
	{
		ProcessKeyboardEvent(raw->data.keyboard);
	}
	if( raw->header.dwType == (raw->header.dwType & RIM_TYPEMOUSE))
	{
		ProcessMouseEvent(raw->data.mouse);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Update that is run every frame of the game </Summary>
/// 
/// <Remarks>	
/// 	This function should be called at the END of every game update
/// 	for key presses to be accurately documented
/// </Remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::FrameUpdate()
{
	for(unsigned i = 0; i < keyChanges.GetSize(); ++i)
	{
		keyMap[keyChanges[i]].ResetMessage();
	}

	keyChanges.Clear();

	for(MouseMap::Iterator iter(mouseMap); !iter.End(); ++iter)
	{
		(*iter).data.ResetFlag();
	}
}

bool InputSystem::CheckForController()
{
	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Process the keyboard event described by rawKeyboard </Summary>
///
/// <Param name="rawKeyboard">	The raw keyboard </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::ProcessKeyboardEvent(const RAWKEYBOARD &rawKeyboard)
{
	KeyboardData current(rawKeyboard, keyMap[rawKeyboard.VKey]);

	keyMap[current.GetKey()] = current;

	keyChanges.Push_Back(current.GetKey());
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Process the mouse event described by rawMouse </Summary>
///
/// <Param name="rawMouse">	The raw mouse </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::ProcessMouseEvent(const RAWMOUSE &rawMouse)
{
	unsigned short key;

	key = MouseData::GetKeyType(rawMouse.usButtonFlags);

	MouseData current(rawMouse, mouseMap[key]);

	mouseMap[current.GetKey()] = current;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key down event for the keyboard </Summary>
///
/// <Param name="key">	The key </Param>
///
/// <Return>	True if the keyboard event is down, false if it is not </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::KeyDown(unsigned short key)
{
	return keyMap[key].GetMessage() == WM_KEYDOWN;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key up event for the keyboard </Summary>
///
/// <Param name="key">	The key </Param>
///
/// <Return>	True if the keyboard event is up, false if it is not </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::KeyUp(unsigned short key)
{
	return keyMap[key].GetMessage() == WM_KEYUP;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key pressed event for the keyboard </Summary>
///
/// <Param name="key">	The key </Param>
///
/// <Return>	
/// 	True if the keyboard event has been pressed this frame, false if it has not
/// </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::KeyPressed(unsigned short key)
{
	return keyMap[key].GetMessage() == WM_KEYDOWN && keyMap[key].GetPrevMessage() == WM_KEYUP;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key released event for the keyboard </Summary>
///
/// <Param name="key">	The key </Param>
///
/// <Return>	
/// 	True if the keyboard event has been released this frame, false if it has not
/// </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::KeyReleased(unsigned short key)
{
	return keyMap[key].GetMessage() == WM_KEYUP && keyMap[key].GetPrevMessage() == WM_KEYDOWN;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key down event for the mouse </Summary>
///
/// <Param name="key">	The key. </Param>
///
/// <Return>	true if it succeeds, false if it fails. </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::MouseDown(unsigned short key)
{
	bool retval = false;

	switch(key)
	{
	case MOUSE_LEFT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_1_DOWN;
		break;
	case MOUSE_RIGHT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_2_DOWN;
		break;
	case MOUSE_MIDDLE:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_3_DOWN;
		break;
	case MOUSE_4:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_4_DOWN;
		break;
	case MOUSE_5:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_5_DOWN;
		break;
	default:
		break;
	}

	return retval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key up event for the mouse </Summary>
///
/// <Param name="key">	The key. </Param>
///
/// <Return>	true if it succeeds, false if it fails. </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::MouseUp(unsigned short key)
{
	bool retval = false;

	switch(key)
	{
	case MOUSE_LEFT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_1_UP;
		break;
	case MOUSE_RIGHT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_2_UP;
		break;
	case MOUSE_MIDDLE:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_3_UP;
		break;
	case MOUSE_4:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_4_UP;
		break;
	case MOUSE_5:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_5_UP;
		break;
	default:
		break;
	}

	return retval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key pressed event for the mouse </Summary>
///
/// <Param name="key">	The key. </Param>
///
/// <Return>	true if it succeeds, false if it fails. </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::MousePressed(unsigned short key)
{
	bool retval = false;

	switch(key)
	{
	case MOUSE_LEFT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_1_DOWN && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_1_UP;
		break;
	case MOUSE_RIGHT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_2_DOWN && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_2_UP;
		break;
	case MOUSE_MIDDLE:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_3_DOWN && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_3_UP;
		break;
	case MOUSE_4:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_4_DOWN && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_4_UP;
		break;
	case MOUSE_5:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_5_DOWN && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_5_UP;
		break;
	default:
		break;
	}

	return retval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Key released event for the mouse </Summary>
///
/// <Param name="key">	The key. </Param>
///
/// <Return>	true if it succeeds, false if it fails. </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
bool InputSystem::MouseReleased(unsigned short key)
{
	bool retval = false;

	switch(key)
	{
	case MOUSE_LEFT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_1_UP && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_1_DOWN;
		break;
	case MOUSE_RIGHT:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_2_UP && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_2_DOWN;
		break;
	case MOUSE_MIDDLE:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_3_UP && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_3_DOWN;
		break;
	case MOUSE_4:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_4_UP && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_4_DOWN;
		break;
	case MOUSE_5:
		retval = mouseMap[key].GetButtonFlag() == RI_MOUSE_BUTTON_5_UP && mouseMap[key].GetPrevButtonFlag() == RI_MOUSE_BUTTON_5_DOWN;
		break;
	default:
		break;
	}

	return retval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Gets the cursor position </Summary>
///
/// <Return>	The cursor position </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
POINT InputSystem::GetCursorPos()
{
	POINT cursor;
	::GetCursorPos(&cursor);
	ScreenToClient(mainHwnd, &cursor);
	return cursor;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Gets the cursor position. </Summary>
///
/// <Param name="ref">	the POINT to be written to </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystem::GetCursorPos(POINT& ref)
{
	::GetCursorPos(&ref);
	ScreenToClient(mainHwnd, &ref);
}